from datetime import datetime

from rest_framework.exceptions import ValidationError
from apps.project.ProjectProcessService import ProjectProcessService
from apps.master.models import CurrencyRateModel, SystemParametersModel
from apps.project.models import ProjectQuotaModel, ProjectDevelopModel, ProjectDevelopAccountModel, \
    ProjectDevelopStageModel, ProjectProcessModel
from apps.projectbases.models import ProjectPlanHModel
from decimal import Decimal

from utils.utils import update_set, update_instance


class DevelopProjectService:
    def __init__(self, request) -> None:
        self.request = request
        super().__init__()


    def initialDevelopProject(self, plan_id:int, quote_id:int):

        quoteQuery = ProjectQuotaModel.objects.filter(id=quote_id, projectPlanFK__id=plan_id).first()
        if quoteQuery is not None:
            # 获取该项目计划书的维护合同的报价单信息
            quote_no = quoteQuery.quote_no  # 报价单号
            quote_date = quoteQuery.quote_date  # 报价日期
            quote_amount = quoteQuery.quote_amount  # 报价金额
            quote_curr = quoteQuery.quote_curr  # 报价币种
            quote_days = quoteQuery.quote_days  # 报价天数

            # 获取该项目计划书的信息
            projectPlanQuery = ProjectPlanHModel.objects.get(id=plan_id)
            standard_curr = projectPlanQuery.standard_curr  # 标准币种
            start_date = projectPlanQuery.start_date  # 项目计划书开始日期
            end_date = projectPlanQuery.end_date  # 项目计划书结束日期
            # 根据标准币种与报价币种获取汇率信息
            currQuery = CurrencyRateModel.objects.filter(standard_curr=standard_curr, quote_curr=quote_curr,
                                                         rate_date__gt=start_date, rate_date__lt=end_date).order_by('-rate_date').first()
            if currQuery is not None:
                rate = currQuery.exchange_rate
            else:
                rate = 1

            # 从系统参数表中获取标准单价
            systemQuery = SystemParametersModel.objects.get(param_code='STANDARDPRICE')
            standard_price = systemQuery.param_value
            # 计算维护合同的各种金额、工数
            sales_date = quote_date  # 预定销售年月
            quote_amount = round(quote_amount / rate, 2)  # 报价金额（标准币种下）
            quoted_days = quote_days  # 报价天数（人日）
            quoted_hours = round(quoted_days * Decimal('7'),2)  # 报价工数(人时)
            a1bugdget_hours = round(quoted_hours * Decimal('0.75'),2)  # A1预算工数(人时)
            a2manage_hours = round(quoted_hours * Decimal('0.05'),2)  # A2预算工数（人时）
            a3risk_hours = 0
            sales_amount = round((a1bugdget_hours + a2manage_hours + a3risk_hours) * Decimal(standard_price),2)  # 预定销售金额
            operating_profit = round(quote_amount - sales_amount,2)  # 营业利润
            operating_margin = round((operating_profit / quote_amount) * Decimal('100'), 2)  # 营业利润率

            data = {
                'sales_date': sales_date,
                'quote_amount': float(quote_amount),
                'sales_amount': float(sales_amount),
                'operating_profit': float(operating_profit),
                'operating_margin': float(operating_margin),
                'quoted_days': float(quoted_days),
                'quoted_hours': float(quoted_hours),
                'a1bugdget_hours': float(a1bugdget_hours),
                'a2manage_hours': float(a2manage_hours),
                'reserve_amount': 0,
                'a3risk_hours': 0
            }
            return data
        else:
            return None

    def initDevelopByQuoteDays(self, quote_days: Decimal, quote_amount: Decimal):
        # 从系统参数表中获取标准单价
        if quote_days is not None and quote_days >= 0:
            systemQuery = SystemParametersModel.objects.get(param_code='STANDARDPRICE')
            standard_price = systemQuery.param_value

            quoted_hours = round(quote_days * Decimal('7'),2)
            a1bugdget_hours = round(quoted_hours * Decimal('0.75'),2)
            a2manage_hours = round(quoted_hours * Decimal('0.05'),2)
            a3risk_hours = 0
            sales_amount = round((a1bugdget_hours + a2manage_hours + a3risk_hours) * Decimal(standard_price),2)

            if quote_amount is not None and quote_amount >= 0:
                operating_profit = round(quote_amount - sales_amount,2)
                operating_margin = round((operating_profit / quote_amount) * Decimal('100'), 2)
                reserve_amount = 0

                data = {
                    'quoted_hours': float(quoted_hours),
                    'a1bugdget_hours': float(a1bugdget_hours),
                    'a2manage_hours': float(a2manage_hours),
                    'a3risk_hours': float(a3risk_hours),
                    'sales_amount': float(sales_amount),
                    'operating_profit': float(operating_profit),
                    'operating_margin': float(operating_margin),
                    'reserve_amount': float(reserve_amount),
                }
                return data
            else:
                data = {
                    'quoted_hours': float(quoted_hours),
                    'a1bugdget_hours': float(a1bugdget_hours),
                    'a2manage_hours': float(a2manage_hours),
                    'a3risk_hours': float(a3risk_hours),
                    'sales_amount': float(sales_amount),
                }
                return data
        else:
            return None

    def validDevelopProject(self, request):
        projectPlanFK = request.data['projectPlanFK']
        project_name = request.data['project_name']
        quoteFK = request.data['quoteFK']
        order_no = request.data['order_no']
        sales_date = request.data['sales_date']
        quote_amount = request.data.get('quote_amount', 0)
        sales_amount = request.data.get('sales_amount', 0)
        operating_profit = request.data.get('operating_profit', 0)
        operating_margin = request.data.get('operating_margin', 0)
        reserve_amount = request.data.get('reserve_amount', 0)
        quoted_days = request.data.get('quoted_days', 0)
        quoted_hours = request.data.get('quoted_hours', 0)
        a1bugdget_hours = request.data.get('a1bugdget_hours', 0)
        a2manage_hours = request.data.get('a2manage_hours', 0)
        a3risk_hours = request.data.get('a3risk_hours', 0)

        plan_query = ProjectPlanHModel.objects.get(id=projectPlanFK)
        plan_status = plan_query.project_status
        start_date = plan_query.start_date
        end_date = plan_query.end_date
        # 只有项目计划书处于生效状态，才能进行项目维护
        if plan_status != 'R':
            raise ValidationError("只有生效状态的项目计划书，才能进行项目维护")
        # 预定销售年月必须处于项目计划书维护期间
        if sales_date:
            sales_date = datetime.strptime(sales_date, "%Y-%m-%d").date()
        else:
            if sales_date < start_date or sales_date > end_date:
                raise ValidationError("预定销售年月必须在项目计划书期间内")
        # 报价金额不能小于0
        if float(quote_amount) < 0:
            raise ValidationError("报价金额必须大于等于0")
        # 预定销售金额不能小于0
        if float(sales_amount) < 0:
            raise ValidationError("预定销售金额必须大于等于0")
        # 营业利润不能小于0
        if float(operating_profit) < 0:
            raise ValidationError("营业利润必须大于等于0")
        # 营业利润率不能小于0
        if float(operating_margin) < 0:
            raise ValidationError("营业利润率必须大于等于0")
        # 预备金额不能小于0
        if float(reserve_amount) < 0:
            raise ValidationError("预备金额必须大于等于0")
        # 报价工数（人日）不能小于0
        if float(quoted_days) < 0:
            raise ValidationError("报价工数（人日）必须大于等于0")
        # 报价工数（人时）不能小于0
        if float(quoted_hours) < 0:
            raise ValidationError("报价工数（人时）必须大于等于0")
        # A1预算工数不能小于0
        if float(a1bugdget_hours) < 0:
            raise ValidationError("A1预算工数（人日）必须大于等于0")
        # A2管理工数（人时）不能小于0
        if float(a2manage_hours) < 0:
            raise ValidationError("A2管理工数（人时）必须大于等于0")
        # A3风险预备工数（人时）不能小于0
        if float(a3risk_hours) < 0:
            raise ValidationError("A3风险预备工数（人时）必须大于等于0")
        # A1+A2+A3 <= quoted_hours * 0.8
        if float(a1bugdget_hours) + float(a2manage_hours) + float(a3risk_hours) > float(quoted_hours) * float(0.8):
            raise ValidationError("A1+A2+A3必须小于等于：报价工时*0.8")

    def createDevelopProjectRequest(self, request):
        projectPlanFK       = request.get('projectPlanFK') # 经过Serialzer处理后的是一个query对象。若传来的仅仅是未处理的数据，则获取的是id
        project_name        = request.get('project_name')
        quoteFK             = request.get('quoteFK')
        order_no            = request.get('order_no')
        sales_date          = request.get('sales_date')
        quote_amount        = request.get('quote_amount', 0)
        sales_amount        = request.get('sales_amount', 0)
        operating_profit    = request.get('operating_profit', 0)
        operating_margin    = request.get('operating_margin', 0)
        reserve_amount      = request.get('reserve_amount', 0)
        quoted_days         = request.get('quoted_days', 0)
        quoted_hours        = request.get('quoted_hours', 0)
        a1bugdget_hours     = request.get('a1bugdget_hours', 0)
        a2manage_hours      = request.get('a2manage_hours', 0)
        a3risk_hours        = request.get('a3risk_hours', 0)
        note                = request.get('note', None)
        user                = self.request.user
        now                 = datetime.now()
        path                = self.request.path_info
        method              = self.request.method

        # 在新建时：项目计划书+报价单号+订单号必须是唯一的
        if ProjectDevelopModel.objects.filter(projectPlanFK__exact=projectPlanFK, quoteFK__exact=quoteFK,
                                               order_no__exact=order_no).exists():
            raise ValidationError("项目计划书+报价单+订单号必须是唯一的")

        develop_data = {
            'projectPlanFK': projectPlanFK,
            'project_name': project_name,
            'project_status': 'O',
            'quoteFK': quoteFK,
            'order_no': order_no,
            'sales_date': sales_date,
            'quote_amount': quote_amount,
            'sales_amount': sales_amount,
            'operating_profit': operating_profit,
            'operating_margin': operating_margin,
            'reserve_amount': reserve_amount,
            'quoted_days': quoted_days,
            'quoted_hours': quoted_hours,
            'a1bugdget_hours': a1bugdget_hours,
            'a2manage_hours': a2manage_hours,
            'a3risk_hours': a3risk_hours,
            'note': note,
            'created': user,
            'created_date': now,
            'created_program': method + ' -> ' + path,
            'updated': user,
            'updated_date': now,
            'updated_program': method + ' -> ' + path
        }
        develop_project = ProjectDevelopModel.objects.create(**develop_data)

        account_data = {
            'projectDevelopFK': develop_project,
            'sales_date':sales_date,
            'sales_amount':sales_amount,
            'amount':0,
            'profit_loss':sales_amount,
            'created': user,
            'created_date': now,
            'created_program': method + ' -> ' + path,
            'updated': user,
            'updated_date': now,
            'updated_program': method + ' -> ' + path
        }
        ProjectDevelopAccountModel.objects.create(**account_data)

        stage_data = {
            'projectDevelopFK': develop_project,
            'sales_date':sales_date,
            'quoted_hours':quoted_hours,
            'bugdget_hours':a1bugdget_hours,
            'actual_hours': 0,
            'margin_hours':a1bugdget_hours,
            'created': user,
            'created_date': now,
            'created_program': method + ' -> ' + path,
            'updated': user,
            'updated_date': now,
            'updated_program': method + ' -> ' + path
        }

        ProjectDevelopStageModel.objects.create(**stage_data)

        # 插入进度记录
        processService = ProjectProcessService(request)
        processService.createProjectProcess('O', develop_project)

        return develop_project


    def updateDevelopProjectRequest(self, develop_list: ProjectDevelopModel, request):
        develop_id          = self.request.data['id']
        projectPlanFK       = request.get('projectPlanFK')
        project_name        = request.get('project_name')
        project_status      = request.get('project_status')
        quoteFK             = request.get('quoteFK')
        order_no            = request.get('order_no')
        sales_date          = request.get('sales_date')
        quote_amount        = request.get('quote_amount', 0)
        sales_amount        = request.get('sales_amount', 0)
        operating_profit    = request.get('operating_profit', 0)
        operating_margin    = request.get('operating_margin', 0)
        reserve_amount      = request.get('reserve_amount', 0)
        quoted_days         = request.get('quoted_days', 0)
        quoted_hours        = request.get('quoted_hours', 0)
        a1bugdget_hours     = request.get('a1bugdget_hours', 0)
        a2manage_hours      = request.get('a2manage_hours', 0)
        a3risk_hours        = request.get('a3risk_hours', 0)
        note                = request.get('note', None)
        user                = self.request.user
        now                 = datetime.now()
        path                = self.request.path_info
        method              = self.request.method

        if project_status != 'O':
            raise ValidationError("只有初始状态的项目才可以被更新")

        # 更新开发项目表
        develop_list.projectPlanFK = projectPlanFK
        develop_list.project_name = project_name
        develop_list.project_status = project_status
        develop_list.quoteFK = quoteFK
        develop_list.order_no = order_no
        develop_list.sales_date = sales_date
        develop_list.quote_amount = quote_amount
        develop_list.sales_amount = sales_amount
        develop_list.operating_profit = operating_profit
        develop_list.operating_margin = operating_margin
        develop_list.reserve_amount = reserve_amount
        develop_list.quoted_days = quoted_days
        develop_list.quoted_hours = quoted_hours
        develop_list.a1bugdget_hours = a1bugdget_hours
        develop_list.a2manage_hours = a2manage_hours
        develop_list.a3risk_hours = a3risk_hours
        develop_list.note = note
        develop_list.updated = user
        develop_list.updated_date = now
        develop_list.updated_program = method + ' -> ' + path
        develop_list.save()

        # 更新项目核算履历
        account_query = ProjectDevelopAccountModel.objects.get(projectDevelopFK=develop_id)
        account_query.sales_amount = sales_amount
        account_query.sales_date = sales_date
        account_query.profit_loss = sales_amount
        account_query.updated = user
        account_query.updated_date = now
        account_query.updated_program = method + ' -> ' + path
        account_query.save()
        # 更新阶段别预实管理
        stage_query = ProjectDevelopStageModel.objects.get(projectDevelopFK=develop_id)
        stage_query.sales_date = sales_date
        stage_query.quoted_hours = quoted_hours
        stage_query.bugdget_hours = a1bugdget_hours
        stage_query.margin_hours = a1bugdget_hours
        stage_query.updated = user
        stage_query.updated_date = now
        stage_query.updated_program = method + ' -> ' + path
        stage_query.save()

        return develop_list

    def deleteDevelopProjectRequest(self, develop_list:ProjectDevelopModel):
        project_id = develop_list.id
        # 删除项目核算履历信息
        accountQuery = ProjectDevelopAccountModel.objects.filter(projectDevelopFK=develop_list)
        for data in accountQuery:
            data.delete()

        # 删除阶段别预实管理信息
        stageQuery = ProjectDevelopStageModel.objects.filter(projectDevelopFK=develop_list)
        for data in stageQuery:
            data.delete()

        # 删除项目进度记录
        query = ProjectProcessModel.objects.filter(projectId=project_id)
        query.delete()

        # 删除开发项目
        develop_list.delete()


    def updateOrigDevelop(self,  old_develop_id, request):
        """
        该方法主要用于拆分开发项目订单时：更新老订单、插入新订单数据
        """
        # 获取原始项目的各项数据
        old_query               = ProjectDevelopModel.objects.get(id=old_develop_id)
        # 获取要拆分的订单的原始数据
        old_quote_amount        = old_query.quote_amount
        old_sales_amount        = old_query.sales_amount
        old_operating_profit    = old_query.operating_profit
        old_operating_margin    = old_query.operating_margin
        old_reserve_amount      = old_query.reserve_amount
        old_quoted_days         = old_query.quoted_days
        old_quoted_hours        = old_query.quoted_hours
        old_a1bugdget_hours     = old_query.a1bugdget_hours
        old_a2manage_hours      = old_query.a2manage_hours
        old_a3risk_hours        = old_query.a3risk_hours

        projectPlanFK   = request.get('projectPlanFK')
        plan_status     = projectPlanFK.project_status
        start_date      = projectPlanFK.start_date
        end_date        = projectPlanFK.end_date

        # 接收画面输入的新项目的各项数据(经过Serializer校验后的转换成正确格式的数据)
        order_no            = request.get('order_no')
        sales_date          = request.get('sales_date')
        quote_amount        = request.get('quote_amount', 0)
        sales_amount        = request.get('sales_amount', 0)
        operating_profit    = request.get('operating_profit', 0)
        operating_margin    = request.get('operating_margin', 0)
        reserve_amount      = request.get('reserve_amount', 0)
        quoted_days         = request.get('quoted_days', 0)
        quoted_hours        = request.get('quoted_hours', 0)
        a1bugdget_hours     = request.get('a1bugdget_hours', 0)
        a2manage_hours      = request.get('a2manage_hours', 0)
        a3risk_hours        = request.get('a3risk_hours', 0)
        user                = self.request.user
        now                 = datetime.now()
        path                = self.request.path_info
        method              = self.request.method

        # 数据校验
        if plan_status != 'R':
            raise ValidationError("只有生效状态的项目计划书，才能进行项目维护")

        if sales_date < start_date or sales_date > end_date:
            raise ValidationError("预定销售年月必须在项目计划书期间内")

        if quote_amount >= old_quote_amount:
            raise ValidationError("报价金额必须小于原始报价金额")

        if sales_amount >= old_sales_amount:
            raise ValidationError("预定销售金额必须小于原始预定销售金额")

        if quoted_days >= old_quoted_days:
            raise ValidationError("报价工数必须小于原始报价工数")

        if quoted_hours >= old_quoted_hours:
            raise ValidationError("报价工时必须小于原始报价工时")

        # 插入新拆分后的项目（包括自动将新项目插入到：项目核算履历+阶段别预实管理中）
        new_develop_list = self.createDevelopProjectRequest(request)

        # 更新原始项目(目前确定是使用减法)
        old_query.quote_amount      = old_quote_amount - quote_amount
        old_query.sales_amount      = old_sales_amount - sales_amount
        old_query.operating_profit  = old_operating_profit - operating_profit
        old_query.reserve_amount    = old_reserve_amount - reserve_amount
        old_query.quoted_days       = old_quoted_days - quoted_days
        old_query.quoted_hours      = old_quoted_hours - quoted_hours
        old_query.a1bugdget_hours   = old_a1bugdget_hours - a1bugdget_hours
        old_query.a2manage_hours    = old_a2manage_hours - a2manage_hours
        old_query.a3risk_hours      = old_a3risk_hours - a3risk_hours
        old_query.updated           = user
        old_query.updated_date      = now
        old_query.updated_program   = method + ' -> ' + path
        old_query.save()

        # 更新原始项目的“项目核算履历”
        old_account = ProjectDevelopAccountModel.objects.get(projectDevelopFK=old_develop_id)
        old_amount = old_account.amount # 原始耗费
        old_account.sales_amount    = old_sales_amount - sales_amount
        old_account.profit_loss     = old_sales_amount - sales_amount - old_amount # 拆分订单后原始订单的损益
        old_account.updated         = user
        old_account.updated_date    = now
        old_account.updated_program = method + ' -> ' + path
        old_account.save()
        # 更新原始项目的“阶段别预实管理”
        old_stage = ProjectDevelopStageModel.objects.get(projectDevelopFK=old_develop_id)
        old_actual_hours            = old_stage.actual_hours # 原始的实绩工数
        old_stage.quoted_hours      = old_quoted_hours - quoted_hours
        old_stage.bugdget_hours     = old_a1bugdget_hours - a1bugdget_hours
        old_stage.margin_hours      = old_a1bugdget_hours - a1bugdget_hours - old_actual_hours
        old_account.updated         = user
        old_account.updated_date    = now
        old_account.updated_program = method + ' -> ' + path
        old_stage.save()

        return new_develop_list


